<?php
/**
* CPAINT (Cross-Platform Asynchronous INterface Toolkit)
*
* http://sf.net/projects/cpaint
*
* released under the terms of the GPL
* see http://www.fsf.org/licensing/licenses/gpl.txt for details
*
*
* proxy script to pass request on to remote servers
*
* @package    CPAINT
* @author     Paul Sullivan <wiley14@gmail.com>
* @author     Dominique Stender <dstender@st-webdevelopment.de>
* @copyright  Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
* @version 	  $Id: cpaint2.proxy.php 9 2009-04-26 06:35:12Z bole.Chen $
*/

//---- includes ----------------------------------------------------------------
	/**
	*	include configuration file
	*/
	require_once(dirname(__FILE__) . '/cpaint2.config.php');

//---- functions ---------------------------------------------------------------

  /**
  * Similar to PHP's builtin parse_url() function, but makes sure what the schema,
  * path and port keys are set to http, /, 80 respectively if they're missing
  *
  * @access     public
  * @param      string    $raw_url    Raw URL to be split into an array
  * @return     array
  */
  function parse_url_clean($raw_url) {
    $retval   = array();
    $raw_url  = (string) $raw_url;

    // make sure parse_url() recognizes the URL correctly.
    if (strpos($raw_url, '://') === false) {
      $raw_url = 'http://' . $raw_url;
    } // end: if

    // split request into array
    $retval = parse_url($raw_url);

    // make sure a path key exists
		if (!isset($retval['path'])) {
		  $retval['path'] = '/';
		} // end: if

		// set port to 80 if none exists
		if (!isset($retval['port'])) {
		  $retval['port'] = '80';
		} // end: if

    return $retval;
  }

  /**
  * Performs a check of the URL to be requested against the whitelist.
  *
  * Returns true if the URL in question if allowed, false otherwise.
  * Expects an array generated by PHP's parse_url() function as input parameter.
  *
  * @access     public
  * @param      array   $request_parts    The URL in question.
  * @return     boolean
  */
  function whitelist_check($request_parts) {
    global $cpaint2_proxy_whitelist;
    $retval           = false;
    $request_check    = '';
    $whitelist_url    = '';
    $whitelist_parts  = array();
    $whitelist_check  = '';

    // iterate over all URLs within the whitelist
    foreach($cpaint2_proxy_whitelist as $whitelist_url) {

      $whitelist_parts = parse_url_clean($whitelist_url);

      if (array_key_exists('path', $whitelist_parts)) {
        // perform a check of host and path if path is part of the whitelist entry
        $whitelist_check = $whitelist_parts['host'] . $whitelist_parts['path'];
        $request_check   = $request_parts['host'] . $request_parts['path'];

      } else {
        // compare only the hosts, path is not part of the whitelist entry
        $whitelist_check = $whitelist_parts['host'];
        $request_check   = $request_parts['host'];
			} // end: if

      // check if whitelist check string marks the beginning of the request check string
			if (strpos($request_check, $whitelist_check) === 0) {
			  $retval = true;
			  break;
			} // end: if
		} // end: foreach

    return $retval;
  }

//---- variables ---------------------------------------------------------------
  $http_response      = '';
  $request_allowed    = false;
  $cp_socket          = false;
  $stream_timeout_s   = floor($cpaint2_config['proxy']['stream_timeout'] / 1000);
  $stream_timeout_ms  = $cpaint2_config['proxy']['stream_timeout'] - $stream_timeout_s * 1000;
  $stream_meta        = array();

  $cp_remote_url      = '';
  $cp_remote_method   = '';
  $cp_remote_query    = '';

  $cp_response_type   = '';

  $cp_request_body    = '';
  $cp_request_header  = '';
  $url_parts          = array();

//---- main code ---------------------------------------------------------------
  // alter PHP's error reporting setting if set
  if ($cpaint2_config['proxy']['security']['error_reporting'] != '') {
    error_reporting($cpaint2_config['proxy']['security']['error_reporting']);
  } // end: if

  // alter maximum runtime according to configuration
  if ($cpaint2_config['proxy']['time_limit'] != -1) {
    set_time_limit($cpaint2_config['proxy']['time_limit']);
  } // end: if

  // determine which method to use (fsockopen or CURL)
  $curl_exists = function_exists('curl_init');
  $fsock_exists = function_exists('fsockopen');

	// check to see if use_curl option is set and within range
	// if not, default to 0 (use fsockopen)
	if ((!isset($cpaint2_config['proxy']['use_curl']))
		|| ($cpaint2_config['proxy']['use_curl'] < 0)
		|| ($cpaint2_config['proxy']['use_curl'] > 3)) {
		$cpaint2_config['proxy']['use_curl'] = 0;
	}

  if ((!$fsock_exists && $cpaint2_config['proxy']['use_curl'] == 0)
  	|| (!$curl_exists && $cpaint2_config['proxy']['use_curl'] == 1)) {
	die('[CPAINT] No method available for connecting to remote server');
  }

	if ($cpaint2_config['proxy']['use_curl'] == 0) $use_fsock = true;
	if ($cpaint2_config['proxy']['use_curl'] == 1) $use_fsock = false;
	if ($cpaint2_config['proxy']['use_curl'] == 2) $use_fsock = $fsock_exists;
	if ($cpaint2_config['proxy']['use_curl'] == 3) $use_fsock = $curl_exists;

  if ($_REQUEST['cpaint_remote_url'] != '') {
    $cp_remote_url      = urldecode($_REQUEST['cpaint_remote_url']);
    $cp_remote_method   = urldecode($_REQUEST['cpaint_remote_method']);
    $cp_remote_query    = urldecode($_REQUEST['cpaint_remote_query']);
    $cp_response_type   = strtoupper($_REQUEST['cpaint_response_type']);
  } // end: if

  // propagate XML header if necessary
  if ($cp_response_type == 'XML'
    || $cp_response_type == 'OBJECT') {

    header('Content-type:  text/xml');
  } // end: if

  // start build HTTP headers
  if ($cp_remote_method == 'GET') {
    $cp_remote_url    .= '?' . $cp_remote_query;

    // prepare parameters
    $url_parts  = parse_url_clean($cp_remote_url);

		if ($use_fsock) {
			// build basic header
			$cp_request_header  = 'GET ' . $url_parts['path'] . '?'
													. str_replace(' ', '+', $url_parts['query'])
													. " HTTP/1.0\r\n"
													. 'Host: ' . $url_parts['host'] . "\r\n";

		} else {
			$curl['url'] 				= $url_parts['path'] . '?' . str_replace(' ', '+', $url_parts['query']);
			$curl['header'][]		= 'Host: ' . $url_parts['host'] . "\r\n";
		}
  } elseif ($cp_remote_method == 'POST') {
    $cp_request_body  = '&' . $cp_remote_query;

    // prepare parameters
    $url_parts  = parse_url_clean($cp_remote_url);

		if ($use_fsock) {
			// build basic header
			$cp_request_header  = 'POST ' . $url_parts['path']  . " HTTP/1.0\r\n"
													. 'Host: ' . $url_parts['host'] . "\r\n"
													. "Content-Type:  application/x-www-form-urlencoded\r\n";
		} else {
			$curl['url'] 				= $url_parts['path'];
			$curl['header'][]		= 'Host: ' . $url_parts['host'] . "\r\n";
		}
  } // end: if

	$content_length_header = 'Content-Length: ' . strlen($cp_request_body) . "\r\n";
	if ($use_fsock) {
		// add content-length header
		$cp_request_header .= $content_length_header;
	} else {
		$curl['header'][] 	= $content_length_header;
	}

  // add authentication to header if necessary
  if (isset($url_parts['user'])
    && $url_parts['user'] != '') {

		if ($use_fsock) {
	    $cp_request_header .= 'Authorization: Basic '
  	                      . base64_encode($url_parts['user'] . ':' . $url_parts['pass']) . "\r\n";
		} else {
			$curl['userauth']			= $url_parts['user'] . ':' . $url_parts['pass'];
		}

  } // end: if

  // perform a check against the whitelist if necessary
  if ($cpaint2_config['proxy']['security']['use_whitelist'] == true) {
    $request_allowed = whitelist_check($url_parts);

  } else {
    // whitelist is disabled, allow any request
    $request_allowed = true;
  } // end: if

  if ($request_allowed == true) {

		if ($use_fsock) {
			// open connection
			$cp_socket = @fsockopen($url_parts['host'], $url_parts['port'], $error, $errstr, $cpaint2_config['proxy']['connect_timeout']);

			if ($cp_socket !== false) {
				// send headers
				@fwrite($cp_socket, $cp_request_header . "\r\n\r\n");

				// send body if necessary
				if ($cp_request_body != '') {
					@fwrite($cp_socket, $cp_request_body . "\r\n");
				} // end: if

				// set stream timeout
				if ($cpaint2_config['proxy']['stream_timeout'] > -1) {
					stream_set_timeout($cp_socket, $stream_timeout_s, $stream_timeout_ms);
				} // end: if

				while (!feof($cp_socket)) {
					$http_response .= fgets($cp_socket);
				} // end: while

				// check whether the request has timed out.
				$stream_meta = stream_get_meta_data($cp_socket);

				if ($stream_meta['timed_out'] == false) {
					list($http_headers, $http_body) = split("\r\n\r\n", $http_response, 2);
					echo($http_body);

				} else {
					die('[CPAINT] The request ran into a timeout.');
				} // end: if

				@fclose($cp_socket);
			} // end: if

		} else {
			// use curl
			$curl_session = curl_init();
			curl_setopt($curl_session, CURLOPT_URL, $curl['url']);
			curl_setopt($curl_session, CURLOPT_HEADER, false);
			curl_setopt($curl_session, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
			curl_setopt($curl_session, CURLOPT_FORBID_REUSE, true);

      if (isset($curl['header'])) {
        curl_setopt($curl_session, CURLOPT_HTTPHEADER, $curl['header']);
      } // end: if

			if (isset($curl['userauth'])) {
				curl_setopt($curl_session, CURLOPT_USERPWD, $curl['userauth']);
				curl_setopt($curl_session, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
			} // end: if
			if ($cp_remote_method == 'GET') {
        curl_setopt($curl_session, CURLOPT_GET, true);
      } // end: if

			if ($cp_remote_method == 'POST') {
				curl_setopt($curl_session, CURLOPT_GET, false);
				curl_setopt($curl_session, CURLOPT_POST, true);
				curl_setopt($curl_session, CURLOPT_POSTFIELDS, $cp_request_body);
			} // end: if

			if ($cpaint2_config['proxy']['connect_timeout'] > -1) {
				curl_setopt($curl_session, CURLOPT_CONNECTTIMEOUT, $cpaint2_config['proxy']['connect_timeout']);
			}  // end: if

			if ($cpaint2_config['proxy']['stream_timeout'] > -1) {
				curl_setopt($curl_session, CURLOPT_TIMEOUT, $stream_timeout_s);
      }  // end: if

			curl_exec($curl_session);

			if (curl_errno($curl_session) != 0) {
				die('[CPAINT] CURL experienced the error "' . curl_error($curl_session) . '"');
			}  // end: if

			curl_close($curl_session);

		}

  } else {
    die('[CPAINT] The host or script cannot be accessed through this proxy.');
  } // end: if

?>